package com.doctcloud.datacloud.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.doctcloud.common.core.domain.R;
import com.doctcloud.common.core.utils.bean.BeanUtils;
import com.doctcloud.datacloud.entity.PatMasterIndexEntity;
import com.doctcloud.datacloud.entity.PatVisitEntity;
import com.doctcloud.datacloud.mapper.PatMasterIndexEntityMapper;
import com.doctcloud.datacloud.mapper.PatVisitEntityMapper;
import com.doctcloud.hisinfo.api.RemoteHisMedrecService;
import com.doctcloud.hisinfo.histable.domain.medrec.PatMasterIndexDo;
import com.doctcloud.hisinfo.histable.domain.medrec.PatVisitDo;
import lombok.RequiredArgsConstructor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.doctcloud.common.core.utils.DateUtils.formatSimpDateWithTime;

@Slf4j
@Service
@RequiredArgsConstructor
public class PatVisitEntityService extends ServiceImpl<PatVisitEntityMapper, PatVisitEntity> {

//    // 将批量大小配置在 application.yml 中，提高灵活性
    @Value("${sync.batch.size}")
    private int BATCH_SIZE;


    private final RemoteHisMedrecService remoteHisMedrecService;
    private final PatMasterIndexEntityMapper patMasterIndexEntityMapper;

    /**
     * 同步指定日期之后的住院记录（按入住日期）。
     *
     * @param staticDate 起始日期
     */
    @Transactional
    public void syncAdminAfterDate(Date staticDate) {
        log.info("--- 开始同步住院记录（入住日期）：入住日期>={} ---", formatSimpDateWithTime(staticDate));
        // 调用通用同步方法，并传入获取“住院”记录的远程服务调用
        syncPatVisits(
                () -> remoteHisMedrecService.adminAfterDate(staticDate),
                "住院",
                staticDate
        );
    }

    /**
     * 同步指定日期之后的出院记录（按出院日期）。
     *
     * @param staticDate 起始日期
     */
    @Transactional
    public void syncDischargeAfterDate(Date staticDate) {
        log.info("--- 开始同步出院记录（出院日期）：出院日期>={} ---", formatSimpDateWithTime(staticDate));
        // 调用通用同步方法，并传入获取“出院”记录的远程服务调用
        syncPatVisits(
                () -> remoteHisMedrecService.dischargeAfterDate(staticDate),
                "出院",
                staticDate
        );
    }

    /**
     * 通用的 PatVisit 同步方法。
     *
     * @param dataSupplier  提供 PatVisitDo 列表的供应商（函数式接口，用于传入不同的远程调用）
     * @param syncType      同步类型描述（如 "住院", "出院"），用于日志
     * @param referenceDate 参考日期，用于日志
     */
    private void syncPatVisits(Supplier<List<PatVisitDo>> dataSupplier, String syncType, Date referenceDate) {
        long startTime = System.currentTimeMillis();
        try {
            // 1. 获取远程数据
            List<PatVisitDo> remotePatVisits = dataSupplier.get();
            if (CollectionUtils.isEmpty(remotePatVisits)) {
                log.info("同步任务终止（{}）：从远程服务获取的记录为空。参考日期: {}", syncType, formatSimpDateWithTime(referenceDate));
                return;
            }
            log.info("从远程服务获取到 {} 条 {} 记录。", remotePatVisits.size(), syncType);

            // 2. 同步关联的患者主数据
            processAndSyncPatientMasterData(remotePatVisits);

            // 3. 转换并分批 Upsert PatVisit 实体
            List<PatVisitEntity> entitiesToUpsert = convertAndFilterPatVisitEntities(remotePatVisits);
            if (CollectionUtils.isNotEmpty(entitiesToUpsert)) {
                batchUpsertPatVisitEntities(entitiesToUpsert, syncType);
            } else {
                log.info("没有需要同步到数据库的 {} 实体记录。", syncType);
            }

            log.info("--- {} 记录同步完成。总耗时：{} ms。参考日期: {} ---",
                    syncType, System.currentTimeMillis() - startTime, formatSimpDateWithTime(referenceDate));

        } catch (Exception e) {
            log.error("同步 {} 记录时发生未预期的异常。参考日期: {}", syncType, formatSimpDateWithTime(referenceDate), e);
            // 异常会导致事务回滚，可以根据需要决定是否向上抛出或在这里处理
            throw e;
        }
    }

    /**
     * 从远程服务获取住院记录（按入住日期）。
     * （此方法现在仅作为一个独立的调用点，如果需要的话可以保留，否则可以删除）
     * @deprecated 建议直接在 syncAdminAfterDate 中使用 lambda
     */
    @Deprecated
    private List<PatVisitDo> getRemotePatVisitsByAdmin(Date staticDate) {
        try {
            return remoteHisMedrecService.adminAfterDate(staticDate);
        } catch (Exception e) {
            String errorMsg = String.format("从远程服务获取住院记录失败，起始时间: %s", formatSimpDateWithTime(staticDate));
            log.error(errorMsg, e);
            return Collections.emptyList();
        }
    }

    /**
     * 从远程服务获取出院记录（按出院日期）。
     * （此方法现在仅作为一个独立的调用点，如果需要的话可以保留，否则可以删除）
     * @deprecated 建议直接在 syncDischargeAfterDate 中使用 lambda
     */
    @Deprecated
    private List<PatVisitDo> getRemotePatVisitsByDischarge(Date staticDate) {
        try {
            return remoteHisMedrecService.dischargeAfterDate(staticDate);
        } catch (Exception e) {
            String errorMsg = String.format("从远程服务获取出院记录失败，起始时间: %s", formatSimpDateWithTime(staticDate));
            log.error(errorMsg, e);
            return Collections.emptyList();
        }
    }

    /**
     * 处理并同步患者主数据。
     */
    private void processAndSyncPatientMasterData(List<PatVisitDo> remotePatVisits) {
        // 1. 提取并去重 patientId
        List<String> allPatientIds = remotePatVisits.stream()
                .map(PatVisitDo::getPatientId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(allPatientIds)) {
            log.info("没有需要同步的患者ID。");
            return;
        }

        // 2. 批量查询本地已存在的患者
        Map<String, PatMasterIndexEntity> existingPatientMap = getExistingPatientMap(allPatientIds);

        // 3. 批量调用远程服务获取患者详细信息
        List<PatMasterIndexDo> remotePatMasters = getRemotePatMasterIndexDos(allPatientIds);
        if (CollectionUtils.isEmpty(remotePatMasters)) {
            log.warn("批量获取患者主数据失败，将跳过患者主数据的同步。");
            return;
        }
        log.info("批量获取到 {} 条患者主数据。", remotePatMasters.size());

        // 4. 分离新增和更新的患者实体
        Map<String, PatVisitDo> patientVisitMap = remotePatVisits.stream()
                .filter(p -> p.getPatientId() != null)
                .collect(Collectors.toMap(PatVisitDo::getPatientId, p -> p, (p1, p2) -> p1)); // 处理重复ID

        List<PatMasterIndexEntity> entitiesToInsert = new ArrayList<>();
        List<PatMasterIndexEntity> entitiesToUpdate = new ArrayList<>();
        Date currentDate = new Date();

        for (PatMasterIndexDo remoteMaster : remotePatMasters) {
            String patientId = remoteMaster.getPatientId();
            PatMasterIndexEntity existingEntity = existingPatientMap.get(patientId);
            PatVisitDo patVisitDo = patientVisitMap.get(patientId);

            PatMasterIndexEntity targetEntity = new PatMasterIndexEntity();
            BeanUtils.copyProperties(remoteMaster, targetEntity);
            targetEntity.setVisitId(patVisitDo != null ? patVisitDo.getVisitId() : null);
            targetEntity.setUpdateDate(currentDate);

            if (existingEntity == null) {
                entitiesToInsert.add(targetEntity);
            } else {
                // 对于更新，需要确保主键或唯一标识被正确设置
                // 假设 patientId 是 PatMasterIndexEntity 的主键或唯一键
                targetEntity.setPatientId(existingEntity.getPatientId());
                targetEntity.setUpdateDate(new Date());
                entitiesToUpdate.add(targetEntity);
            }
        }

        // 5. 执行批量插入和更新
        if (CollectionUtils.isNotEmpty(entitiesToInsert)) {
            patMasterIndexEntityMapper.batchInsert(entitiesToInsert);
            log.info("批量插入 {} 条患者主数据。", entitiesToInsert.size());
        }
        if (CollectionUtils.isNotEmpty(entitiesToUpdate)) {
            patMasterIndexEntityMapper.batchUpdate(entitiesToUpdate);
            log.info("批量更新 {} 条患者主数据。", entitiesToUpdate.size());
        }
    }

    /**
     * 批量获取远程患者主数据。
     */
    private List<PatMasterIndexDo> getRemotePatMasterIndexDos(List<String> patientIds) {
        try {
            R<List<PatMasterIndexDo>> response = remoteHisMedrecService.getPatMasterIndexByIds(patientIds);
            if (R.isSuccess(response) && CollectionUtils.isNotEmpty(response.getData())) {
                return response.getData();
            } else {
                log.error("批量获取患者主数据失败，远程服务返回: {}", response);
            }
        } catch (Exception e) {
            log.error("批量获取患者主数据时发生异常。请求的PatientIds数量: {}", patientIds.size(), e);
        }
        return Collections.emptyList();
    }

    /**
     * 查询本地已存在的患者并转换为 Map。
     */
    private Map<String, PatMasterIndexEntity> getExistingPatientMap(List<String> patientIds) {
        LambdaQueryWrapper<PatMasterIndexEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.in(PatMasterIndexEntity::getPatientId, patientIds);
        List<PatMasterIndexEntity> existingPatients = patMasterIndexEntityMapper.selectList(queryWrapper);
        log.info("本地数据库中已存在 {} 条患者主数据。", existingPatients.size());
        return existingPatients.stream()
                .collect(Collectors.toMap(PatMasterIndexEntity::getPatientId, p -> p));
    }

    /**
     * 转换并过滤 PatVisit DO 到 Entity。
     */
    private List<PatVisitEntity> convertAndFilterPatVisitEntities(List<PatVisitDo> remotePatVisits) {
        return remotePatVisits.stream()
                .filter(doObj -> doObj.getPatientId() != null && !doObj.getPatientId().trim().isEmpty())
                .filter(doObj -> doObj.getVisitId() != null)
                .map(this::convertDoToEntity)
                .collect(Collectors.toList());
    }

    /**
     * 分批执行 PatVisit Entity 的 Upsert 操作。
     */
    private void batchUpsertPatVisitEntities(List<PatVisitEntity> entitiesToUpsert, String syncType) {
        log.info("准备分批执行 {} PatVisit 批量 Upsert 操作，总记录数: {}, 每批大小: {}", syncType, entitiesToUpsert.size(), BATCH_SIZE);
        int totalAffectedRows = 0;
        for (int i = 0; i < entitiesToUpsert.size(); i += BATCH_SIZE) {
            int end = Math.min(i + BATCH_SIZE, entitiesToUpsert.size());
            List<PatVisitEntity> batchList = entitiesToUpsert.subList(i, end);
            totalAffectedRows += baseMapper.batchUpsert(batchList);
            log.info("{} PatVisit 同步进度: {}/{}", syncType, end, entitiesToUpsert.size());
        }
        log.info("{} PatVisit 批量 Upsert 操作完成，数据库受影响的总行数: {}", syncType, totalAffectedRows);
    }

    /**
     * DO 转 Entity。
     */
    private PatVisitEntity convertDoToEntity(PatVisitDo doObj) {
        PatVisitEntity entity = new PatVisitEntity();
        BeanUtils.copyProperties(doObj, entity);
        return entity;
    }
}